package com.vr.xuecheng.content.handler.jobhandler;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.vr.xuecheng.base.exception.XueChengPlusException;
import com.vr.xuecheng.base.util.MultipartFileUtil;
import com.vr.xuecheng.content.model.vo.CoursePreviewVO;
import com.vr.xuecheng.content.service.ICoursePublishService;
import com.vr.xuecheng.feign.client.CourseIndexClient;
import com.vr.xuecheng.feign.client.MediaClient;
import com.vr.xuecheng.feign.dto.CourseIndex;
import com.vr.xuecheng.feign.response.Result;
import com.vr.xuecheng.messagesdk.model.entity.MqMessage;
import com.vr.xuecheng.messagesdk.service.MessageProcessAbstract;
import com.vr.xuecheng.messagesdk.service.MqMessageService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

/**
 * @author hzh
 * @date 2025/03/21
 * 同步课程发布信息的任务
 */
@Log4j2
@Component
@RequiredArgsConstructor
public class CoursePublishTask extends MessageProcessAbstract {
    @Value("${spring.thymeleaf.prefix}")
    private String prefix;
    @Value("${spring.thymeleaf.suffix}")
    private String suffix;
    @Value("${spring.thymeleaf.mode}")
    private String templateMode;
    @Value("${spring.thymeleaf.encoding}")
    private String templateEncoding;
    private final ICoursePublishService coursePublishService;
    private final MediaClient mediaClient;
    private final CourseIndexClient courseIndexClient;
    private final StringRedisTemplate stringRedisTemplate;


    @Override
    public boolean execute(MqMessage mqMessage) {
        long courseId = Long.parseLong(mqMessage.getBusinessKey1());
        MqMessageService mqMessageService = getMqMessageService();
        CoursePreviewVO coursePreviewInfo = coursePublishService.getCoursePreviewInfo(courseId);
        // 定时任务的业务逻辑，将发布数据同步到elasticsearch、redis让后将发布课程静态化
        this.generateCourseHtml(mqMessage, mqMessageService, courseId, coursePreviewInfo);
        // 同步到redis缓存中
        this.saveCourseCache(mqMessage, mqMessageService, courseId, coursePreviewInfo);
        // 同步到es中
        this.saveCourseIndex(mqMessage, mqMessageService, courseId,coursePreviewInfo);
        return true;

    }


    /**
     * @param mqMessage        执行任务的内容
     * @param mqMessageService mq消息服务
     * @param courseId         课程id
     * @param courseInfo       要存储的信息
     */
    private void generateCourseHtml(MqMessage mqMessage, MqMessageService mqMessageService, long courseId, CoursePreviewVO courseInfo) {
        int stageOne = mqMessageService.getStageOne(mqMessage.getId());
        //课程静态化
        if (stageOne == 1) {
            log.debug("课程静态化已完毕，课程id：{}", courseId);
            return;
        }
        log.debug("开始课程静态化，课程id：{}", courseId);
        // 初始化化模板
        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(this.getClass().getClassLoader());
        templateResolver.setPrefix("/templates/");
        templateResolver.setSuffix(suffix);
        templateResolver.setTemplateMode(templateMode);
        templateResolver.setCharacterEncoding(templateEncoding);
        //创建模板引擎
        TemplateEngine templateEngine = new TemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);

        // 准备页面所需的动态数据
        Context context = new Context();
        context.setVariable("model", courseInfo);
        // 渲染模板并生成文件
        // 使用 ByteArrayOutputStream 替代 StringWriter
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        OutputStreamWriter writer = null;
        boolean isUpload = false;
        try {
            // 将模板写入字节流中
            writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
            templateEngine.process("course_template", context, writer);
            writer.flush();
            // 远程调用将html文件上传到minio中
            MultipartFile file = this.htmlToMultiPartFile(outputStream.toByteArray(), "file");
            mediaClient.upload(file, "course/" + courseId + ".html");
        } catch (Exception e) {
            log.error("课程静态化失败，课程id：{}", courseId);
            throw new RuntimeException(e.getMessage());
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        // 完成一阶段
        mqMessageService.completedStageOne(mqMessage.getId());
        log.debug("课程静态化完成，课程id：{}", courseId);
    }

    private MultipartFile htmlToMultiPartFile(byte[] htmlBytes, String fileName) {
        return MultipartFileUtil.build(fileName, htmlBytes, "text/html");

    }

    /**
     * @param mqMessage        执行任务的内容
     * @param mqMessageService mq消息服务
     * @param courseId         保存课程信息到redis中
     * @param courseInfo       要存储的信息
     */
    private void saveCourseCache(MqMessage mqMessage, MqMessageService mqMessageService, long courseId, CoursePreviewVO courseInfo) {

        int stageTwo = mqMessageService.getStageTwo(mqMessage.getId());
        if (stageTwo == 1) {
            log.debug("课程同步redis缓存已完毕，课程id：{}", courseId);
            return;
        }
        log.debug("开始同步到redis缓存中，课程id：{}", mqMessage.getBusinessKey1());
        try {
            ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
            valueOperations.set("course:" + courseId, JSON.toJSONString(courseInfo), 30, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.error("课程同步redis缓存失败，课程id：{}", courseId);
            throw new RuntimeException(e.getMessage());
        }
        mqMessageService.completedStageTwo(mqMessage.getId());
        log.debug("课程同步redis缓存完成，课程id：{}", courseId);
    }

    /**
     * @param mqMessage         执行任务的内容
     * @param mqMessageService  消息服务
     * @param courseId          课程id
     * @param courseInfo 保存的信息
     * 保存到es中
     */
    private void saveCourseIndex(MqMessage mqMessage, MqMessageService mqMessageService, long courseId, CoursePreviewVO courseInfo) {
        int stageThree = mqMessageService.getStageThree(mqMessage.getId());
        if (stageThree == 1) {
            log.debug("课程同步es已完毕，课程id：{}", courseId);
            return;
        }
        log.debug("开始保存课程索引到es中，课程id：{}", mqMessage.getBusinessKey1());
        CourseIndex courseIndex = BeanUtil.toBean(courseInfo.getCourseBase(), CourseIndex.class);
        Result<String> result = courseIndexClient.addCourseIndex(courseIndex);
        if (!Result.isSuccess(result)) {
            log.error("课程同步es失败，课程id：{}", courseId);
            XueChengPlusException.cast("保存课程失败");
        }
        mqMessageService.completedStageThree(mqMessage.getId());
        log.debug("课程同步es完成，课程id：{}", courseId);
    }

    /**
     * 定时任务处理器
     */
    @XxlJob("coursePublishJobHandler")
    public void coursePublishJobHandler() {
        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();
        int count = Runtime.getRuntime().availableProcessors();
        this.process(shardIndex, shardTotal, "course_publish", count, 60);
    }
}
